package com.glsc.ngateway.opmanage.service.report.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.glsc.ngateway.common.base.domain.postgre6.ExcelGzb;
import com.glsc.ngateway.common.base.domain.mysql.opmanage.report.fileupload.ImportTemplateRulesDefine;
import com.glsc.ngateway.common.base.repo.postgre6.ExcelGzbRepo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class ExcelGzbListener extends AnalysisEventListener<Map<Integer, String>> {

    private static final Logger logger = LoggerFactory.getLogger(ExcelGzbListener.class);
    private static final int BATCH_COUNT = 3000;
    private static final String FUND_NAME_SEP = "_";
    private static final Pattern FUND_NAME_COMPILE = Pattern.compile("_.*_");
    private static final Pattern DATE_COMPILE = Pattern.compile("[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}");
    List<ExcelGzb> list = new ArrayList<>();

    private List<ImportTemplateRulesDefine> rulesDefineList;
    private ExcelGzbRepo excelGzbRepo;
    private Boolean clearFlag = false;
    private String fundName;
    private String date;

    public ExcelGzbListener() {
    }

    public ExcelGzbListener(ExcelGzbRepo excelGzbRepo, List<ImportTemplateRulesDefine> rulesDefineList) {
        this.excelGzbRepo = excelGzbRepo;
        if (!CollectionUtils.isEmpty(rulesDefineList)) {
            this.rulesDefineList = rulesDefineList.stream().filter(item -> item.getSheetIndex().equals(0)).collect(Collectors.toList());
        }
    }

    public String getFundName() {
        return fundName;
    }

    public String getDate() {
        return date;
    }

    /**
     * 解析规则：
     * 估值日期：解析单元格A3
     * 基金名称：解析单元格A2
     * 其余字段：按列对应，全部字符串（空行也需要存储），从A5开始读取
     *
     * @param data
     * @param analysisContext
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
        analysisContext.readWorkbookHolder().setIgnoreEmptyRow(false);
        ReadRowHolder readRowHolder = analysisContext.readRowHolder();
        Map<Integer, Cell> cellMap = readRowHolder.getCellMap();
        Integer rowIndex = readRowHolder.getRowIndex();
        if (rowIndex == 1) {
            Matcher matcher = FUND_NAME_COMPILE.matcher(data.get(0));
            if (matcher.find()) {
                fundName = matcher.group().replace(FUND_NAME_SEP, "");
            }
        } else if (rowIndex == 2) {
            Matcher matcher = DATE_COMPILE.matcher(data.get(0));
            if (matcher.find()) {
                date = matcher.group();
            }
            list.add(new ExcelGzb(date, fundName, rowIndex + 1, data.get(0), null,
                    null, null, null, null, null, data.get(7),
                    null, null, data.get(10), null));
        } else if (rowIndex >= 4) {
            /**
             * 由于统一使用string接收，导致部分字段即可能是string，也可能是double
             * 且double值可能展示为科学计数法、数字包含'，'、负数用括号展示等问题，需要额外处理
             */
            CellData amount = (CellData) cellMap.get(2);
            CellData cost = (CellData) cellMap.get(4);
            CellData val = (CellData) cellMap.get(7);
            CellData valtInc = (CellData) cellMap.get(9);

            String amountNew = amount == null ? data.get(2) : amount.toString();
            String costNew = cost == null ? data.get(4) : cost.toString();
            String valNew = val == null ? data.get(7) : val.toString();
            String valtIncNew = valtInc == null ? data.get(9) : valtInc.toString();

            try {
                amountNew = StringUtils.hasText(amountNew) ? amountNew : null;
                amountNew = new BigDecimal(amountNew).toPlainString();
            } catch (Exception e) {
//                logger.info("第{}行数据无法将【数量】字段值：{}转为数字", rowIndex + 1, amountNew);
            }
            try {
                costNew = StringUtils.hasText(costNew) ? costNew : null;
                costNew = new BigDecimal(costNew).toPlainString();
            } catch (Exception e) {
//                logger.info("第{}行数据无法将【成本】字段值：{}转为数字", rowIndex + 1, costNew);
            }
            try {
                valNew = StringUtils.hasText(valNew) ? valNew : null;
                valNew = new BigDecimal(valNew).toPlainString();
            } catch (Exception e) {
//                logger.info("第{}行数据无法将【市值】字段值：{}转为数字", rowIndex + 1, valNew);
            }
            try {
                valtIncNew = StringUtils.hasText(valtIncNew) ? valtIncNew : null;
                valtIncNew = new BigDecimal(valtIncNew).toPlainString();
            } catch (Exception e) {
//                logger.info("第{}行数据无法将【估值增值】字段值：{}转为数字", rowIndex + 1, valtIncNew);
            }

            list.add(new ExcelGzb(date, fundName, rowIndex + 1, data.get(0), data.get(1),
                    amountNew, data.get(3), costNew, data.get(5), data.get(6), valNew, data.get(8),
                    valtIncNew, data.get(10), data.get(11)));
        }

        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    //读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        TemplateCheckHandler.check(context, rulesDefineList);
    }

    //读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 去除末尾的空行
        ExcelGzb lastItem = list.get(list.size() - 1);
        if (lastItem.getSubjectCode() == null) {
            list.remove(lastItem);
        }
        saveData();
    }

    private void saveData() {
        if (!clearFlag) {
            clearFlag = true;
            excelGzbRepo.delete(date, fundName);
        }
        excelGzbRepo.saveAllAndFlush(list);
    }

}
